package io.zhengsh.redis.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.zhengsh.redis.annotation.MultiCache;
import io.zhengsh.redis.service.MultiCacheService;
//import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Aspect
@Component
public class MultiCacheAspect {

    private final static Logger logger = LoggerFactory.getLogger(MultiCacheAspect.class);

    @Autowired
    private MultiCacheService multiCacheService;
    private final static SpelExpressionParser parser = new SpelExpressionParser();
    private final static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();


    @Pointcut("@annotation(io.zhengsh.redis.annotation.MultiCache)")
    public void multiCache() {
        // Pointcut
    }

    @Around("multiCache()")
    public Object multiCacheSupport(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        MultiCache multiCache = method.getAnnotation(MultiCache.class);
        String mkey = generateKey(multiCache.key(), pjp);
        try {
            if ("read".equals(multiCache.op())) {
                String retVal = multiCacheService.read(mkey);
                if (retVal != null && !"".equals(retVal.trim()) && !"null".equals(retVal.trim())) {
                    logger.info("multiCache invoke key: {}, val:{}", mkey, retVal);
                    return JSON.parseObject(retVal, method.getReturnType());
                }
            }
            Object proceed = pjp.proceed();
            multiCacheService.write(mkey, "delete".equals(multiCache.op()) ? "" : JSON.toJSONString(proceed));
            return proceed;
        } catch (Throwable ex) {
            logger.info("multiCache err key: {}", mkey, ex);
            throw ex;
        }
    }

    /**
     * 通过 el 表达式解析 key
     *
     * @param key
     * @param joinPoint
     * @return
     */
    private String generateKey(String key, ProceedingJoinPoint joinPoint) {
        // 通过joinPoint获取被注解方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        // 解析过后的Spring表达式对象
        Expression expression = parser.parseExpression(key);
        // spring的表达式上下文对象
        EvaluationContext context = new StandardEvaluationContext();
        // 通过joinPoint获取被注解方法的形参
        Object[] args = joinPoint.getArgs();
        // 给上下文赋值
        if (paramNames != null) {
            for (int i = 0; i < args.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        // 表达式从上下文中计算出实际参数值
        return Objects.requireNonNull(expression.getValue(context)).toString();
    }
}
